/***********************************************************/
/*        code in this file runs after monitor enter       */
/*        protect mode,it's the second stage of monitor    */
/***********************************************************/

#include "elf.h"
#include "monitor.h"

/* kernel is loaded in physical address 0x8000*/
#define KERN_FROM_PA			0x8000
/* kernel will be moved to physical address 0x100000*/
#define KERN_TO_PA				0x100000

/* we load (0x39 + 0x3F) sectors in total*/
#define KERN_SEC				(0x39 + 0x3F)
/* size of all kernel sector that has been loaded*/
#define KERN_SIZE				(KERN_SEC * 512)

/*
 * get the ELF header and program header of kernel,
 * so we can map the page right.
 */
 #if 0
int kern_hdr_offset(void){
	Elf32_Ehdr *kern_p = (Elf32_Ehdr *)KERN_FROM_PA;
	sys_info.kern_hdr_offset = kern_p->e_phentsize * kern_p->e_phnum 
		+ sizeof(Elf32_Ehdr);
	return 0;
}
#endif 

int kern_entry_offset(void){
	uint_32 *entry_offset = (uint_32*)((uint_8*)&sys_info.kern_entry + MONITOR_BASE);
	Elf32_Ehdr *elf_hdr = (Elf32_Ehdr*)KERN_FROM_PA;
	Elf32_Addr p_hdr =  (Elf32_Addr)elf_hdr + (Elf32_Off)elf_hdr->e_phoff;
	*entry_offset = get_entry_offset(p_hdr, elf_hdr->e_entry, 
			elf_hdr->e_phentsize,  elf_hdr->e_phnum);
	return 0;
}

int move_kernel(void){
	uint32_t *from = (uint_32 *)KERN_FROM_PA;
	uint32_t *to = (uint_32 *)KERN_TO_PA;
	int i;

	for (i = 0; i < (KERN_SIZE / 4); i++){
		*(to + i) = *(from + i);
	}
	return 0;
}

#define PG_SIZE		0x1000

#define PT_P 		0x00000001
#define PT_R		0x00000000
#define PT_RW		0x00000002
#define PT_S		0x00000000
#define PT_U		0x00000004
#define PT_PWB		0x00000000
#define PT_PWT		0x00000008
#define PT_PCD		0x00000000
#define PT_S		0x00000000

#define PG_P 		0x00000001
#define PG_R		0x00000000
#define PG_RW		0x00000002
#define PG_S		0x00000000
#define PG_U		0x00000004
#define PG_PWB		0x00000000
#define PG_PWT		0x00000008
#define PG_PCD		0x00000000
#define PG_S		0x00000000

uint_32 pg_dir[1024] __attribute__ ((aligned(0x1000)));
uint_32 pg_tbl[2][1024] __attribute__ ((aligned(0x1000)));

int pre_set_pg_dir(void){
	uint_32 *pg_pointer = (uint_32*)((char *)pg_dir + MONITOR_BASE);
	pg_pointer[0] = (uint_32)&(pg_tbl[0][0]) + MONITOR_BASE + PT_P + PT_RW + PT_U;
	pg_pointer[0x300] = (uint_32)&(pg_tbl[1][0]) + MONITOR_BASE + PT_P + PT_RW + PT_U;
	return 0;
}

int pre_set_pg_tbl(void){
	int i;
	uint_32 phy_base = 0x00000000;
	uint_32 *pg_pointer = NULL;
	uint_32 *kern_entry = (uint_32 *)((uint_32)&(sys_info.kern_entry) + MONITOR_BASE);

	/*
	 * the physical address between 0M and 1M maps the
	 * virtual address equally.
	 */
	pg_pointer = (uint_32 *)((uint_32)&(pg_tbl[0][0]) + MONITOR_BASE);
	for (i = 0; i < 256; i++, phy_base += PG_SIZE){
		*(pg_pointer + i) = phy_base + PG_P + PG_RW + PG_U;
	}


	/*
	 * now we want to map the kernel's page, virtual address
	 * is 0xC0000000,before that, we need find the first instruction's
	 * address.
	 */
	pg_pointer = (uint_32 *)((uint_32)&(pg_tbl[1][0]) + MONITOR_BASE);
	phy_base = KERN_TO_PA + *kern_entry;
	for (i = 0; i < 256; i++, phy_base += PG_SIZE){
		*(pg_pointer + i) = phy_base + PG_P + PG_RW + PG_U;
	}
	return 0;
}

int pre_set_page(void){
	pre_set_pg_dir();
	pre_set_pg_tbl();
	return 0;
}

